package com.cplexValue;


import ilog.concert.*;
import ilog.cplex.IloCplex;

import java.util.*;

public class cplex {
    IloCplex model;
    double cost;
    private IloRange[] range ;
    private IloNumVar[][][] x;
    private IloNumVar[] y;
    private static ArrayList<int[]> setO;//o、q、j，
    private static ArrayList<int[]> goalJ;//目标矩形块所对应索引、价值、次数
    private static ArrayList<int[]> allJ;//所有可能矩形块
    private long runTime;
    private ArrayList<int[]> LocationAndSize;
    private com.cplexValue.data data;

    public long getRunTime() {
        return runTime;
    }

    public ArrayList<int[]> getLocationAndSize() {
        return LocationAndSize;
    }

    public cplex(com.cplexValue.data data1) {
        this.data = data1;
    }

    public void runModel(int reductionsMethod) throws IloException {
        buildModel(reductionsMethod);
        long startTime = System.currentTimeMillis();
        if (!model.solve()) {
            System.out.println("problem should not solve false!!!");
        } else {
            long endTime = System.currentTimeMillis();
            cost = model.getObjValue();
            runTime = endTime - startTime;
            System.out.println("final solution:"+cost);

            double[] duals = model.getDuals(range);
            for (int i = 0; i < duals.length; i++) {
//                if (duals[i]!=0){
                    System.out.println("range"+i+":"+duals[i]);
//                }
            }

            System.out.println("y值为");
            for (int[] jValue:goalJ){
                System.out.println("y"+jValue[0]+"="+model.getValue(y[jValue[0]]));
            }
            HashMap<Integer,int[]> result_Xindex = new HashMap<>();//索引，【方向，切割距离，切割次数】
            for (int[] index : setO) {
                int xValue = (int) model.getValue(x[index[0]][index[1]][index[2]]);
                if (xValue != 0) {//获取切割方式
                    result_Xindex.put(index[2],new int[]{index[0], index[1], xValue});
                }
            }
            System.out.println();

//            LocationAndSize=getResult(result_Xindex);
//            getresult result = new getresult(result_Xindex, cutSet);
//            result.get_size(0, 0, 0);
//            ArrayList<int[]> tmpRec = result.getResult_size();
//            lastRec=new ArrayList<>();
//            for (int[] rec : tmpRec) {
//                for (int i = 0; i <= Data.getK(); i++) {
//                    if (rec[2]== l[i] &&rec[3]==w[i]){
//                        lastRec.add(rec);
//                    }
//                }
//            }
//            System.out.println(lastRec.size());
//            new RectangleVisualizationJFrame(lastRec)
                //getResult();
            }
            model.end();
    }

    private void buildModel(int reductionsMethod) throws IloException {
        model = new IloCplex();
        setO=data.getSetO();
        goalJ=data.getGoalJ();
        allJ=data.getAllJ();
        range=new IloRange[allJ.size()];

        IloNumExpr expr1 = model.linearNumExpr();
        if (reductionsMethod==1){
            y = new IloIntVar[allJ.size()];
            for (int[] jValue:goalJ){
                y[jValue[0]]=model.intVar(0,Integer.MAX_VALUE,"y"+jValue[0]);
                model.addLe(y[jValue[0]], jValue[2]);// 添加约束（ 5、7 ）
                if (jValue[1]!=0){
                    expr1 =model.sum(expr1,model.prod(y[jValue[0]],jValue[1]));
                }
            }
            model.addMaximize(expr1);//添加目标函数（1）

            x=new IloIntVar[2][Math.max(allJ.get(0)[0],allJ.get(0)[1])][allJ.size()];
            for (int[] list:setO){//setO为可能切割
                x[list[0]][list[1]][list[2]]=model.intVar(0,Integer.MAX_VALUE,"x"+list[0]+list[1]+"_"+list[2]);
            }//(6)
        }else {
            y = new IloNumVar[allJ.size()];
            for (int[] jValue:goalJ){
                y[jValue[0]]=model.numVar(0,Integer.MAX_VALUE,"y"+jValue[0]);
                model.addLe(y[jValue[0]], jValue[2]);// 添加约束（ 5、7 ）
                if (jValue[1]!=0){
                    expr1 =model.sum(expr1,model.prod(y[jValue[0]],jValue[1]));
                }
            }
            model.addMaximize(expr1);//添加目标函数（1）

            x=new IloNumVar[2][Math.max(allJ.get(0)[0],allJ.get(0)[1])][allJ.size()];
            for (int[] list:setO){//setO为可能切割
                x[list[0]][list[1]][list[2]]=model.numVar(0,Integer.MAX_VALUE,"x"+list[0]+list[1]+"_"+list[2]);
            }//(6)
        }



        IloNumExpr expr4=model.linearNumExpr();
        for (int[] list:setO){
            if (list[2]==0){
                expr4=model.sum(expr4,x[list[0]][list[1]][list[2]]);
            }
        }
        range[0]=model.addLe(model.sum(expr4,y[0]),1);//（4）

        //(2)
        HashSet<Integer> goalIndex=new HashSet<>();
        for (int[] goal:goalJ) {
            goalIndex.add(goal[0]);
        }//获取目标矩形块对应索引
        goalIndex.remove(0);
        for (int index :goalIndex) {
            IloNumExpr expr2_2 =model.linearNumExpr();
            IloNumExpr expr2_1= model.linearNumExpr();
            for (int[] list:setO){
                if (list[2]==index) {
                    expr2_2 = model.sum(expr2_2, x[list[0]][list[1]][list[2]]);
                }
            }
            for (int[] list1 :setO){
                expr2_1= model.sum(expr2_1, model.prod(x[list1[0]][list1[1]][list1[2]],aJudge(list1[0], list1[1], list1[2],index,allJ)));
            }
//            range[index]=model.addGe(model.sum(expr2_1, model.prod(-1,expr2_2)),y[index]);
            range[index]=model.addGe(model.sum(model.sum(expr2_1, model.prod(-1,expr2_2)),model.prod(y[index],-1)),0);
        }

        //（3）
        for (int j = 1; j < allJ.size(); j++) {
            if (!goalIndex.contains(j)){
                IloNumExpr expr3_2= model.linearNumExpr();
                IloNumExpr expr3_1= model.linearNumExpr();
                for (int[] listK:setO){
                    expr3_1= model.sum(expr3_1,model.prod(x[listK[0]][listK[1]][listK[2]],aJudge(listK[0],listK[1],listK[2],j,allJ)));
                }
                for (int[] list : setO){
                    if (j ==list[2]){
                        expr3_2= model.sum(expr3_2,x[list[0]][list[1]][j]);
                    }
                }
//                model.addGe(model.sum(expr3_1, model.prod(-1,expr3_2)),0);
                range[j]=model.addLe(model.sum(expr3_1, model.prod(-1,expr3_2)),0);
            }
        }
        model.exportModel("model.lp");
    }

    //HashMap<Integer,int[]>indexX：索引，【方向，切割距离，切割次数】
    public ArrayList<int[]> getResult(HashMap<Integer,int[]>indexX) throws IloException {//{j--o/q/num}
        List<ArrayList<antNode>> locations=new ArrayList<>();
        antNode first=new antNode(0,0,new int[]{0,0});
        ArrayList<antNode> tmp=new ArrayList<>();
        tmp.add(first);
        locations.add(tmp);
        List<ArrayList<antNode>> result=iter(locations,indexX);
        ArrayList<int[]> resultLocationAndSize=new ArrayList<>();
        for (ArrayList<antNode> innerList : result) {
            for (antNode node : innerList) {
                System.out.println(node.index+"("+allJ.get(node.index)[0]+","+allJ.get(node.index)[1]+")"+"--"+ Arrays.toString(node.location));
                resultLocationAndSize.add(new int[]{node.location[0],node.location[1],
                        allJ.get(node.index)[0],allJ.get(node.index)[1]});
            }
        }
        return resultLocationAndSize;
    }


    //HashMap<Integer,int[]>indexX：索引，【方向，切割距离，切割次数】
    private List<ArrayList<antNode>> iter(List<ArrayList<antNode>> locations,HashMap<Integer,int[]>indexX){
        ArrayList<antNode> tmpIndex=locations.get(locations.size()-1);
        ArrayList<antNode> nextIndex=new ArrayList<>();
        int[] rec1;int[] rec2;
        for (antNode node:tmpIndex){
            int[] cutMethod=indexX.get(node.index);//o/q/num
            if (cutMethod.length==0){break;}
            if (cutMethod[2]==0){
                break;
            }else {
                if (cutMethod[0]==1){
                    int newW=allJ.get(node.index)[1];
                    int newL1=cutMethod[1];
                    int newL2=allJ.get(node.index)[0]-cutMethod[1];
                    rec1=new int[]{newL1,newW};
                    rec2=new int[]{newL2,newW};
                    int index1=findIndex(rec1);
                    int index2=findIndex(rec2);
                    if(indexX.containsKey(index1)){
                        antNode node1=new antNode(node.index,index1, node.location);
                        nextIndex.add(node1);
                    }
                    if(indexX.containsKey(index2)){
                        antNode node2=new antNode(node.index,index2, new int[]{node.location[0]+cutMethod[1],node.location[1]});
                        nextIndex.add(node2);
                    }
                }else {
                    int newL=allJ.get(node.index)[0];
                    int newW1=cutMethod[1];
                    int newW2=allJ.get(node.index)[1]-cutMethod[1];
                    rec1=new int[]{newL,newW1};
                    rec2=new int[]{newL,newW2};
                    int index1=findIndex(rec1);
                    int index2=findIndex(rec2);
                    if(indexX.containsKey(index1)){
                        antNode node1=new antNode(node.index,index1, node.location);
                        nextIndex.add(node1);
                    }
                    if(indexX.containsKey(index2)){
                        antNode node2=new antNode(node.index,index2, new int[]{node.location[0],node.location[1]+cutMethod[1]});
                        nextIndex.add(node2);
                    }
                }
                indexX.get(node.index)[2]--;
            }
        }
        if (nextIndex.isEmpty()){return locations;}
        locations.add(nextIndex);
        iter(locations,indexX);
        return locations;
    }
    private int findIndex(int[] target) {
        for (int i = 0; i < allJ.size(); i++) {
            if (allJ.get(i)[0]==target[0]&&allJ.get(i)[1]==target[1]) {
                return i;
            }
        }
        return -1; // 如果未找到，返回 -1
    }



    //切k所得j的块数
    private static double aJudge(int o, int q, int k, int j, ArrayList<int[]> cutSet_Copy) {
        int[] k_set = cutSet_Copy.get(k);
        int[] j_set = cutSet_Copy.get(j);
        if (o == 1) {
            if (j_set[1] == k_set[1] && q == j_set[0] && (k_set[0] - q) == j_set[0]) return 2.0;
            if (j_set[1] == k_set[1] && (q == j_set[0] || (k_set[0] - q) == j_set[0])) return 1.0;
        }else {
            if (k_set[0] == j_set[0] && q == j_set[1] && (k_set[1] - q) == j_set[1]) return 2.0;
            if (k_set[0] == j_set[0] && (q == j_set[1] || (k_set[1] - q) == j_set[1])) return 1.0;
        }
        return 0.0;
    }
}